﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using Application = System.Windows.Application;
using ListBox = System.Windows.Controls.ListBox;
using MessageBox = System.Windows.Forms.MessageBox;

namespace ChangeTextWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            ComboEncoding.Items.Add("Please select...");
            ComboEncoding.SelectedIndex = 0;
            foreach (var encodingInfo in Encoding.GetEncodings().OrderBy(e => e.Name))
            {
                ComboEncoding.Items.Add(new EncodingInfoExtended(encodingInfo));
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            LblAutoEncoding.Content = Encoding.Default.HeaderName;
        }

        private void BtnAddFolder_Click(object sender, RoutedEventArgs e)
        {
            var folderBrowser = new FolderBrowserDialog();
            folderBrowser.ShowDialog();
            if (!IsFileOrDirectoryAlreadyInList(ListFolders, folderBrowser.SelectedPath))
            {
                ListFolders.Items.Add(folderBrowser.SelectedPath);
            }

        }

        private void BtnDeleteFolder_Click(object sender, RoutedEventArgs e)
        {

            var items = ListFolders.SelectedItems;
            var copyOfItems = items.Cast<object>().ToList();
            foreach (var copyOfItem in copyOfItems)
            {
                ListFolders.Items.Remove(copyOfItem);
            }
        }

        private bool IsFileOrDirectoryAlreadyInList(ListBox currentListBox, string newPath)
        {
            foreach (string item in currentListBox.Items)
            {
                if (item == newPath)
                {
                    return true;
                }
            }
            return false;
        }

        private void BtnAddFile_Click(object sender, RoutedEventArgs e)
        {
            var fileDialog = new OpenFileDialog { Multiselect = true };
            fileDialog.ShowDialog();
            foreach (var fileName in fileDialog.FileNames)
            {
                if (!IsFileOrDirectoryAlreadyInList(ListFiles, fileName))
                {
                    ListFiles.Items.Add(fileName);
                }
            }

        }

        private void BtnDeleteFile_Click(object sender, RoutedEventArgs e)
        {
            var items = ListFiles.SelectedItems;
            var copyOfItems = items.Cast<object>().ToList();
            foreach (var copyOfItem in copyOfItems)
            {
                ListFiles.Items.Remove(copyOfItem);
            }
        }

        private void BtnExit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void BtnReplace_Click(object sender, RoutedEventArgs e)
        {
            var filePaths = new List<string>();
            Encoding encoding;
            if (!GetFilePaths(filePaths)) return;
            if (!CheckTextToFind()) return;
            if (!CheckEncoding(out encoding)) return;

            string result = DoMainWork(filePaths, TxtFind.Text, TxtReplace.Text, encoding);

            var resultWindow = new ResultWindow {ShowInTaskbar = false, TxtResults = {Text = result}};
            resultWindow.ShowDialog();
        }

        private string DoMainWork(IEnumerable<string> filePaths, string textToFind, string textToReplaceWith, Encoding encoding)
        {
            var sb = new StringBuilder();
            foreach (var filePath in filePaths)
            {
                var attributes = new Attributes(filePath, textToFind, textToReplaceWith, encoding);
                try
                {
                    sb.AppendLine(FindAndReplaceInFile(attributes));
                }
                catch (Exception ex)
                {
                    sb.AppendLine(ex.Message+"\n");
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// Replaces text in single file
        /// </summary>
        /// <param name="attributes"></param>
        private static string FindAndReplaceInFile(Attributes attributes)
        {
            // check existence
            if (!File.Exists(attributes.FilePath))
            {
                throw new InvalidOperationException(string.Format("File not exists. Check file path \r\n'{0}'.",
                    attributes.FilePath));
            }
            // check access permissions
            new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, attributes.FilePath).Demand();
            // read file data
            int numberOfChanges;
            
            List<string> data = File.ReadAllLines(attributes.FilePath, attributes.Encoding).ToList();

            UpdateData(data, attributes.TextToFind, attributes.TextToReplace, out numberOfChanges);

            if (numberOfChanges > 0)
            {
                File.WriteAllLines(attributes.FilePath, data, attributes.Encoding);
            }
            return string.Format("{0} lines changed in file:\n{1,5}{2}\n",numberOfChanges," ",attributes.FilePath);
        }

        /// <summary>
        /// Updates data
        /// </summary>
        /// <param name="data"></param>
        /// <param name="textToFind"></param>
        /// <param name="textToReplace"></param>
        /// <param name="numberOfChanges"></param>
        private static void UpdateData(List<string> data, string textToFind, string textToReplace,
            out int numberOfChanges)
        {
            numberOfChanges = 0;
            for (int i = 0; i < data.Count; i++)
            {
                if (data[i].Contains(textToFind))
                {
                    data[i] = data[i].Replace(textToFind, textToReplace);
                    numberOfChanges += 1;
                }
            }
        }

        private bool CheckTextToFind()
        {
            if (string.IsNullOrEmpty(TxtFind.Text))
            {
                MessageBox.Show("Specify text to be found first", "Error", MessageBoxButtons.OK);
                return false;
            }
            return true;
        }

        private bool CheckEncoding(out Encoding encoding)
        {
            encoding = Encoding.Default;
            if (ComboEncoding.SelectedIndex == 0)
            {
                var result = MessageBox.Show(
                    string.Format("You haven't specified any encoding. Auto-detected '{0}' will be used. Sure?",
                        Encoding.Default.HeaderName), "Encoding alert", MessageBoxButtons.OKCancel);
                if (result == System.Windows.Forms.DialogResult.Cancel)
                {
                    return false;
                }
            }
            else
            {
                encoding = ((EncodingInfoExtended)ComboEncoding.SelectedItem).Encoding;
                var result = MessageBox.Show(
                    string.Format("You have specified '{0}' encoding. Sure?",
                        encoding.HeaderName), "Encoding alert", MessageBoxButtons.OKCancel);
                if (result == System.Windows.Forms.DialogResult.Cancel)
                {
                    return false;
                }
            }
            return true;
        }

        private bool GetFilePaths(List<string> filePaths)
        {
            foreach (var path in ListFiles.Items)
            {
                filePaths.Add(path.ToString());
            }
            foreach (var item in ListFolders.Items)
            {
                filePaths.AddRange(Directory.GetFiles(item.ToString(), "*", SearchOption.AllDirectories));
            }
            if (filePaths.Any()) return true;
            MessageBox.Show("No files to replace in found", "Error", MessageBoxButtons.OK);
            return false;
        }
    }
}
